namespace Smarts.Internal.Test.Sequences
{
    using System;
    using System.Linq;
    using NUnit.Framework;
    using Smarts.Sequences;

    [TestFixture]
    public class IntegersTests
    {
        [Test]
        public void BasicSequence()
        {
            var ints1To100 = Integers.From(1).To(100);
            var j = 0;
            foreach (var i in ints1To100)
            {
                j++;
                Assert.That(i, Is.EqualTo(j));
            }

            Assert.That(j, Is.EqualTo(100));
        }

        [Test]
        public void BasicSequenceWithStep()
        {
            var evens2To100 = Integers.From(2).To(100).By(2);
            var j = 0;
            foreach (var i in evens2To100)
            {
                j += 2;
                Assert.That(i, Is.EqualTo(j));
            }

            Assert.That(j, Is.EqualTo(100));
        }

        [Test]
        public void BasicSequenceWithLimit()
        {
            var ints1To50 = Integers.From(1).First(50);
            var j = 0;
            foreach (var i in ints1To50)
            {
                j++;
                Assert.That(i, Is.EqualTo(j));
            }

            Assert.That(j, Is.EqualTo(50));
        }

        [Test]
        public void BasicSequenceWithOrigin()
        {
            Integers.Origin = 50;
            var ints50To100 = Integers.UpTo(100);
            var j = 49;
            foreach (var i in ints50To100)
            {
                j++;
                Assert.That(i, Is.EqualTo(j));
            }

            Assert.That(j, Is.EqualTo(100));
        }

        [Test]
        public void ReverseSequence()
        {
            var ints100To1 = Integers.From(100).To(1);
            var j = 101;
            foreach (var i in ints100To1)
            {
                j--;
                Assert.That(i, Is.EqualTo(j));
            }

            Assert.That(j, Is.EqualTo(1));
        }

        [Test]
        public void ReverseSequenceWithStep()
        {
            var evens100To2 = Integers.From(100).To(2).By(2);
            var j = 102;
            foreach (var i in evens100To2)
            {
                j -= 2;
                Assert.That(i, Is.EqualTo(j));
            }

            Assert.That(j, Is.EqualTo(2));
        }

        [Test]
        public void ReverseSequenceWithStepFromByTo()
        {
            var evens100To2 = Integers.From(100).By(2).To(2);
            var j = 102;
            foreach (var i in evens100To2)
            {
                j -= 2;
                Assert.That(i, Is.EqualTo(j));
            }

            Assert.That(j, Is.EqualTo(2));
        }

        [Test]
        public void ReverseSequenceNoEndFirstBy()
        {
            var first100Negatives = Integers.From(-1).First(100).By(-1);
            var j = 0;
            foreach (var i in first100Negatives)
            {
                j--;
                Assert.That(i, Is.EqualTo(j));
            }

            Assert.That(j, Is.EqualTo(-100));
        }

        [Test]
        public void ReverseSequenceNoEndByFirst()
        {
            var first100Negatives = Integers.From(-1).By(-1).First(100);
            var j = 0;
            foreach (var i in first100Negatives)
            {
                j--;
                Assert.That(i, Is.EqualTo(j));
            }

            Assert.That(j, Is.EqualTo(-100));
        }

        [Test]
        public void StepSequenceWithNoEndFirstBy()
        {
            var first50Evens = Integers.From(2).First(50).By(2);
            var j = 0;
            foreach (var i in first50Evens)
            {
                j += 2;
                Assert.That(i, Is.EqualTo(j));
            }

            Assert.That(j, Is.EqualTo(100));
        }

        [Test]
        public void StepSequenceWithNoEndByFirst()
        {
            var first50Evens = Integers.From(2).By(2).First(50);
            var j = 0;
            foreach (var i in first50Evens)
            {
                j += 2;
                Assert.That(i, Is.EqualTo(j));
            }

            Assert.That(j, Is.EqualTo(100));
        }

        [Test]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void SequenceCantIncrementByZero()
        {
            Integers.From(1).By(0);
        }

        [Test]
        public void BasicHasWithPositiveIntegers()
        {
            var positive = Integers.From(0);
            Assert.That(positive.Has(0), Is.True, "Zero is a positive number");
            Assert.That(positive.Has(-1), Is.Not.True, "-1 is not a positive number");
            Assert.That(positive.Has(50345345), Is.True, "A large number is a positive number");
        }

        [Test]
        public void BasicHasWithNegativeIntegers()
        {
            var negative = Integers.From(-1).By(-1);
            Assert.That(negative.Has(0), Is.Not.True, "Zero is not a negative number");
            Assert.That(negative.Has(-1), Is.True, "-1 is a negative number");
            Assert.That(negative.Has(4353534), Is.Not.True, "A large number is not a negative number");
            Assert.That(negative.Has(-1345453), Is.True, "A large negative number is a negative number");
        }

        [Test]
        public void BasicHasWithPositiveLimit()
        {
            var oneToTen = Integers.From(1).To(10);
            Assert.That(oneToTen.Has(0), Is.Not.True, "Sequence does not have 0");
            for (var i = 1; i <= 10; i++)
            {
                Assert.That(oneToTen.Has(i), Is.True, "Sequence has " + i);
            }

            Assert.That(oneToTen.Has(0), Is.Not.True, "Sequence does not have 11");

            var fifteenToTwentyFive = Integers.From(15).To(25);
            Assert.That(fifteenToTwentyFive.Has(14), Is.Not.True, "Sequence does not have 14");
            for (var i = 15; i <= 25; i++)
            {
                Assert.That(fifteenToTwentyFive.Has(i), Is.True, "Sequence has " + i);
            }

            Assert.That(fifteenToTwentyFive.Has(26), Is.Not.True, "Sequence does not have 26");
        }

        [Test]
        public void BasicSequenceWithNegativeLimit()
        {
            var negativeFifteenToNegativeFive = Integers.From(-15).To(-5);
            Assert.That(negativeFifteenToNegativeFive.Has(-16), Is.Not.True, "Sequence does not have -16");
            for (var i = -15; i <= -5; i++)
            {
                Assert.That(negativeFifteenToNegativeFive.Has(i), Is.True, "Sequence has " + i);
            }

            Assert.That(negativeFifteenToNegativeFive.Has(-4), Is.Not.True, "Sequence does not have -4");
        }

        [Test]
        public void BasicSequenceCrossingZero()
        {
            var negativeFifteenToPositiveFifteen = Integers.From(-15).To(15);
            Assert.That(negativeFifteenToPositiveFifteen.Has(-16), Is.Not.True, "Sequence does not have -16");
            for (var i = -15; i <= 15; i++)
            {
                Assert.That(negativeFifteenToPositiveFifteen.Has(i), Is.True, "Sequence has " + i);
            }

            Assert.That(negativeFifteenToPositiveFifteen.Has(16), Is.Not.True, "Sequence does not have 16");
        }

        [Test]
        public void BasicReverseSequence()
        {
            var negativeFiveToNegativeFifteen = Integers.From(-5).To(-15);
            Assert.That(negativeFiveToNegativeFifteen.Has(-4), Is.Not.True, "Sequence does not have -4");
            for (var i = -5; i >= -15; i--)
            {
                Assert.That(negativeFiveToNegativeFifteen.Has(i), Is.True, "Sequence has " + i);
            }

            Assert.That(negativeFiveToNegativeFifteen.Has(-16), Is.Not.True, "Sequence does not have -16");
        }

        [Test]
        public void BasicSequenceHasWithStep()
        {
            var evensTo100 = Integers.From(2).By(2).To(100);
            var even = false;
            for (var i = 1; i <= 101; i++)
            {
                Assert.That(evensTo100.Has(i), Is.EqualTo(even), i + " is even");
                even = !even;
            }

            Assert.That(evensTo100.Has(102), Is.False, "102 is out of range of the sequence");
        }

        [Test]
        public void BasicReverseHasWithStep()
        {
            var negativeEvens = Integers.From(-2).By(2).To(-100);
            var even = false;
            for (var i = -1; i >= -101; i--)
            {
                Assert.That(negativeEvens.Has(i), Is.EqualTo(even), i + " is even");
                even = !even;
            }

            Assert.That(negativeEvens.Has(-102), Is.False, "-102 is out of range of the sequence");
        }

        [Test]
        public void BasicReverseHasWithStepNoLimit()
        {
            var negativeEvens = Integers.From(-2).By(-2);
            var even = false;
            for (var i = -1; i >= -1001; i--)
            {
                Assert.That(negativeEvens.Has(i), Is.EqualTo(even), i + " is even");
                even = !even;
            }
        }

        [Test]
        public void SequenceCanBeUsedTwice()
        {
            var sequence = Integers.From(1).To(100);
            var firstUse = sequence.ToList();
            var secondUse = sequence.ToList();
            Assert.That(secondUse, Is.EquivalentTo(firstUse));
            Assert.That(firstUse.Count, Is.EqualTo(100));
            Assert.That(secondUse.Count, Is.EqualTo(100));
        }
    }
}